using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.IO;
using System.Collections;
using System.Threading;
using System.Configuration;
using System.Timers;

using TPDSConfig;
using TPDSTAMS;

namespace ImageMonitor
{
    public partial class ImageMonitorService : ServiceBase
    {
        private FileSystemWatcher filewatcher;
        private ImageMonitorClass imageMonitor = new ImageMonitorClass();
        private System.Timers.Timer imageTimer = null;

        public ImageMonitorService()
        {
            InitializeComponent();

            // Create timer object.
            double interval = 0.0;
            try
            {
                interval = Convert.ToDouble( ConfigurationManager.AppSettings[ "RecheckInterval" ] );
                imageTimer = new System.Timers.Timer( interval );
                imageTimer.Elapsed += new System.Timers.ElapsedEventHandler( OnTimedEvent );
            }
            catch
            {
                // Ignore and don't create timer.
            }
        }        
        
        protected override void OnStart(string[] args)
        {
            System.Diagnostics.EventLog.WriteEntry("ImageMonitor", "Image Monitor started");
            System.Diagnostics.Debugger.Launch();
            TPDSConfig.TPDSConfig configFile = imageMonitor.ProcessStart( );

            if( configFile != null )
            {
                // Add file watcher
                filewatcher = new FileSystemWatcher( );
                filewatcher.Path = Path.Combine( ConfigurationManager.AppSettings[ "WebServerPath" ], configFile.ImageMonitorRootFolder );
                filewatcher.IncludeSubdirectories = true;
                filewatcher.Filter = "*.set";
                filewatcher.Created += new FileSystemEventHandler( imageset_Created );
                filewatcher.EnableRaisingEvents = true;
                System.Diagnostics.EventLog.WriteEntry( "ImageMonitor", "Added Filewatcher - " + filewatcher.Path );

                // Maybe start timer
                if( imageTimer != null )
                {
                    imageTimer.Start( );
                    System.Diagnostics.EventLog.WriteEntry( "ImageMonitor", "Added Timer " );
                }
            }
            else
            {
                System.Diagnostics.EventLog.WriteEntry("ImageMonitor", "No configuration xml file could be found", EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Handle Service stopped
        /// </summary>
        protected override void OnStop( )
        {
            // Remove file watcher.
            if( filewatcher != null )
            {
                filewatcher.EnableRaisingEvents = false;
                filewatcher.Dispose( );
            }

            // Maybe stop timer
            if( imageTimer != null )
                imageTimer.Stop( );
        }

        /// <summary>
        /// Handle new image set file created...
        /// </summary>
		private void imageset_Created(object sender, FileSystemEventArgs e)
		{
            // Protection on this whole routine to prevent service from crashing should this routine throw an exception
            try
            {
                imageMonitor.ProcessImageSet( e.FullPath );
            }
            catch( Exception ex )
            {
                TPDSConfig.ErrorLog.WriteError( ex.Message, string.Empty );
            }
        }

        /// <summary>
        /// Handles timer event and calls routine to process waiting imagesets.
        /// </summary>
        private void OnTimedEvent( object source, ElapsedEventArgs e )
        {
            // Protection on this whole routine to prevent service from crashing should this routine throw an exception
            try
            {
                imageMonitor.ProcessWaitingImageSets( );
            }
            catch( Exception ex )
            {
                TPDSConfig.ErrorLog.WriteError( ex.Message, string.Empty );
            }
        }
    }

    
    public class ImageMonitorClass
    {
        private TPDSConfig.TPDSConfig ConfigFile;
        private TAMSDBAccess DataAccess;
        private string AbsoluteWebServerPath;
        private string AbsoluteImagePath;
        
        /// <summary>
        /// Perform startup proceedures.
        /// </summary>
        public TPDSConfig.TPDSConfig ProcessStart( )
        {
            // Load xml configuration file
            ConfigFile = TPDSConfig.TPDSConfig.Load( ConfigurationManager.AppSettings[ "WebServerPath" ] );
            if( ConfigFile == null )
                return null;

            // Create data access object
            DataAccess = new TAMSDBAccess( ConfigurationManager.ConnectionStrings[ "TAMSDB" ].ToString( ) );

            // Record absolute path to images within web site.
            AbsoluteWebServerPath = ConfigurationManager.AppSettings[ "WebServerPath" ];
            AbsoluteImagePath = Path.Combine( AbsoluteWebServerPath, ConfigFile.ImageMonitorRootFolder );

            // Return config class so that the calling routine can perform actions specifically required when hosted as a service.
            return ConfigFile;
        }

        /// <summary>
        /// Process a particular *.set file 
        /// </summary>
        public void ProcessImageSet( string fullPath )
        {
            // Must have run the initialisation code first.
            if( ConfigFile == null )
                return;

            System.Diagnostics.EventLog.WriteEntry( "ImageMonitor", "Process Imageset - " + fullPath );

            TPDSControl.ImageSet newImageset = TPDSControl.ImageSet.LoadOne( fullPath );
            // Sometimes the *.set file could be empty...
            if( newImageset == null )
                return;

            string previousBarcode = string.Empty;

            foreach( TPDSControl.Image image in newImageset.Images ) //scan for barcodes
            {
                string barcode = String.Empty;
                try
                {
                    //won't use returned name, already assumed thumbnail filename when generating imageset
                    TPDSFileIO.ImageProcessingIO.CreateThumbnail( Path.Combine( Path.GetDirectoryName( newImageset.Filepath ), Path.GetFileName( image.Filename ) ), ConfigFile.ThumbnailMaxDimension );

                    barcode = TPDSFileIO.ImageProcessingIO.ScanImage( image, ConfigFile );

                    if( ( !String.IsNullOrEmpty( barcode ) ) && ( barcode.Trim( ).ToLower( ) != previousBarcode ) )
                    {
                        previousBarcode = barcode.Trim( ).ToLower( );

                        image.IsBarcode = true;

                        // General Goods items have a barcode that contains both the consignment vendor no and consignment good no, separated by a '*' character.
                        if( barcode.Contains( "*" ) )
                        {
                            image.ConsignmentNo = barcode;

                            // Extract goods no from TAMS DB
                            QueryGeneralGoodsNo( image );
                        }

                        // Normal processing for all other items
                        else
                            image.BarcodeNo = barcode;
                    }
                    else
                    {
                        image.IsBarcode = false;
                    }
                }
                catch( Exception ex )
                {
                    TPDSConfig.ErrorLog.WriteError( ex.Message, string.Empty );
                    image.Status = TPDSControl.Image.ImageStatus.Error;
                }

                newImageset.Save( );
            }
        }

        /// <summary>
        /// Queries TAMS DB to find General Goods, GoodNo....
        /// </summary>
        private void QueryGeneralGoodsNo( TPDSControl.Image image )
        {
            // Separate the vendor and goods numbers.
            int locn = image.ConsignmentNo.IndexOf( '*' );
            string vendorNo = image.ConsignmentNo.Substring( 0, locn );
            string goodNo = image.ConsignmentNo.Substring( locn + 1 );

            // If both valid numbers, query DB
            if( !String.IsNullOrEmpty( vendorNo ) && !String.IsNullOrEmpty( goodNo ) )
            {
                // Extract goods no from TAMS DB
                int? goodsno = DataAccess.GetGoodNoFromGeneralGoods( vendorNo, goodNo );
                if( goodsno.HasValue )
                    image.BarcodeNo = goodsno.Value.ToString( );
            }
        }

        /// <summary>
        /// Processes all waiting imagesets irrespective of the folder they're in.
        /// </summary>
        public void ProcessWaitingImageSets( )
        {
            // Must have run the initialisation code first.
            if( ConfigFile == null )
                return;

            // System.Diagnostics.EventLog.WriteEntry( "ImageMonitor", "Processing Waiting ImageSets" );

            // Look for all *.set files within the web site's image root folder.
            DirectoryInfo drInfo = new DirectoryInfo( this.AbsoluteImagePath );
            FileInfo[ ] files = drInfo.GetFiles( "*.set", SearchOption.AllDirectories );

            foreach( FileInfo file in files )
            {
                string suffix = "aa";
                string previousConsignment = string.Empty;
                string previousGoodNo = string.Empty;

                try
                {
                    TPDSControl.ImageSet imageset = TPDSControl.ImageSet.LoadOne( file.FullName );
                    if( imageset == null )
                        continue;

                    foreach( TPDSControl.Image image in imageset.Images )
                    {
                        // Look for waiting images only
                        if( !image.IsWaiting || !image.ConsignmentNo.Contains( "*" ) )
                            continue;

                        try
                        {
                            if( image.ConsignmentNo.Trim( ).ToLower( ) != previousConsignment )
                            {
                                // Extract goods no from TAMS DB
                                QueryGeneralGoodsNo( image );

                                // Reset suffix
                                suffix = "aa";

                                // Reset reocrded values.
                                previousConsignment = image.ConsignmentNo.Trim( ).ToLower( );
                                previousGoodNo = image.BarcodeNo;
                            }
                            else
                                // Record our current goods no.
                                image.BarcodeNo = previousGoodNo;
                            
                            if( String.IsNullOrEmpty( image.BarcodeNo ) )
                                continue;

                            string oldExtention = Path.GetExtension( image.Filename );
                            string newFilename = String.Empty;
                            
                            // If not a barcode image, rename and move to destination folder
                            if( !image.IsBarcode )
                            {
                                newFilename = image.BarcodeNo + suffix + oldExtention;
                                suffix = GetNextSuffix(suffix);
                                image.Move( this.AbsoluteWebServerPath, Path.Combine( ConfigFile.DestinationFolder, newFilename ) );
                            }

                            // Permanently remove image.
                            else
                                image.Delete( this.AbsoluteWebServerPath );

                            image.DeleteFromImageset( );
                        }
                        catch( Exception ex )
                        {
                            TPDSConfig.ErrorLog.WriteError( ex.Message, string.Empty );
                        }
                    }
                }
                catch( Exception ex )
                {
                    TPDSConfig.ErrorLog.WriteError( ex.Message, string.Empty );
                }
            }
        }
        private string GetNextSuffix(string currentSuffix)
        {
            if (currentSuffix.Length != 2)
                throw new Exception("Incorrect Suffix format.");
            char char1 = currentSuffix[0];
            char char2 = currentSuffix[1];

            if (char2 == 'z')
            {
                char1++;
                char2 = 'a';
                return String.Concat(char1, char2);
            }
            char2++;

            return string.Concat(char1, char2);
        }
    }

}
